package br.unisul.redeNeural;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;




/**
 * Rede Neural Madaline, esta rede é uma rede multicamadas, 
 * porem ela tem apenas uma camada intermediaria, alem da camada de entrada e saida.<br>
 * Se na {@link Configuracao} possuir mais de uma {@link ConfiguracaoCamadaIntemediaria}, elas serão removidas.<br>
 * Se na {@link Configuracao} não possuir nenhuma {@link ConfiguracaoCamadaIntemediaria} então sera adicionada mais uma 
 * {@link ConfiguracaoCamadaIntemediaria} com a mesma quantidade de neuronios que na de saida
 * @author Joelson
 *
 */
public class Madaline extends RedeNeural implements java.io.Serializable{


	/**
	 * 
	 */
	private static final long serialVersionUID = -4820691388989768650L;

	public Madaline(Configuracao configuracao) {
		super(configuracao);
	}

	public void treinar(ParTreino valoresTreino) throws RedeNeuralException {
		getConfiguracao().setMultiCamadas(true);
		if(getConfiguracao().getCamadasIntermediarias().size() > 1){
			for (int i = 1; i < getConfiguracao().getCamadasIntermediarias().size(); i++) {
				getConfiguracao().getCamadasIntermediarias().remove(i);
			}
		}else if(getConfiguracao().getCamadasIntermediarias().size() <1 ){
			getConfiguracao().addCamadaIntermediaria(new ConfiguracaoCamadaIntemediaria(getConfiguracao().getQuantidadeSaida()));
		}
		inicializarValores();
		/*
		 * Os pesos entre a camada intermediaria e a camada de saida
		 * dever ser igual a 0.5
		 */
		Peso [][] tempPesos = getPesos(1);
		for (Peso[] pesos2 : tempPesos) {
			for (Peso peso : pesos2) {
				peso.setValor(.5);
			}
		}
		while(!verificarParada()){
			for (int i = 0; i < valoresTreino.getQuantidade(); i++) {

				atualizarEntradas(valoresTreino.getEntrada(i));

				/**
				 * variavel necessaria para pegar os valores da camada intermediaria
				 * para depois poder calcular a atualização do peso
				 */
				double [] z_in = new double[getNeuronios(1).length];

				for (int j = 1; j < quantidadeCamadas(); j++) {
					Neuronio[] neuronios = getNeuronios(j);
					for (int k = 0; k < neuronios.length; k++) {
						Neuronio[] neuroniosEntradas = getNeuroniosEntrada(j, k);
						Peso[] pesoEntradas = getPesosEntrada(j, k);
						double d = calcularSaidaNeuronio(neuroniosEntradas, pesoEntradas, neuronios[k]);
						if(j == 1){
							z_in[k] = d;
						}
						neuroniosEntradas[k].setValorNeuronio(aplicarFuncaoAtivacao(d));
					}
				}
				Neuronio[] neuroniosSaidas = getSaidas();
				Neuronio[] neuroniosEntradas = getEntradas();
				double[] saidasDesejadas = valoresTreino.getSaida(i);

				for (int j = 0; j < neuroniosSaidas.length; j++) {
					if(saidasDesejadas[j] != neuroniosSaidas[j].getValorNeuronio()){
						if(saidasDesejadas[j] < 0){
							Neuronio[] camadaIntermediaria = getNeuronios(1);
							for (int k = 0; k < camadaIntermediaria.length; k++) {
								if(camadaIntermediaria[k].getValorNeuronio() > 0){
									Peso[] pesos = getPesosEntrada(1, k);
									for (int l = 0; l < pesos.length; l++) {
										atualizarPeso(pesos[l], getConfiguracao().getTaxaAprendizado() *
												(saidasDesejadas[j]- z_in[k])*neuroniosEntradas[l].getValorNeuronio());
									}
									atualizarVies(camadaIntermediaria[k].getVies(), getConfiguracao().getTaxaAprendizado() *
											(saidasDesejadas[j]- z_in[k]));
								}
							}
						}else{
							Neuronio[] camadaIntermediaria = getNeuronios(1);
							List<Neuronio> neuronios = new ArrayList<Neuronio>(Arrays.asList(camadaIntermediaria));
							Neuronio menor = Collections.min(neuronios);
							int k = neuronios.indexOf(menor);
								Peso[] pesos = getPesosEntrada(1, k);
								for (int l = 0; l < pesos.length; l++) {
									atualizarPeso(pesos[l], getConfiguracao().getTaxaAprendizado() *
											(saidasDesejadas[j]- z_in[k])*neuroniosEntradas[l].getValorNeuronio());
								}
								atualizarVies(camadaIntermediaria[k].getVies(), getConfiguracao().getTaxaAprendizado() *
										(saidasDesejadas[j]- z_in[k]));
						}
					}
				}
			}
		}
	}



}
